# 文件名: voice_assistant.py (已修正并解决写字打断和自我对话问题)
import sys
import os
import time
import threading
import queue
import json
import inspect
import re
import random

try:
    import keyboard
except ImportError:
    print("❌ keyboard 库未安装，请先执行：pip install keyboard")
    sys.exit(1)
try:
    import pyaudio
except ImportError:
    print("❌ PyAudio 库未安装，请先执行：pip install PyAudio")
    sys.exit(1)

from dashscope import MultiModalConversation
from dashscope.multimodal.dialog_state import DialogState
from dashscope.multimodal.multimodal_dialog import MultiModalDialog, MultiModalCallback
from dashscope.multimodal.multimodal_request_params import (
    Upstream, Downstream, ClientInfo, RequestParameters, Device,
    RequestToRespondParameters
)
from config.config import *

class AudioPlayer:
    def __init__(self, callback_handler):
        self.pyaudio_instance = pyaudio.PyAudio()
        self.stream = None
        self.audio_queue = queue.Queue()
        self.is_playing = True
        self.play_thread = threading.Thread(target=self._play_worker, daemon=True)
        self._init_stream()
        self.play_thread.start()

    def _init_stream(self):
        if self.stream:
            try: 
                self.stream.stop_stream()
                self.stream.close()
            except: 
                pass
        self.stream = self.pyaudio_instance.open(
            format=self.pyaudio_instance.get_format_from_width(SAMPLE_WIDTH),
            channels=CHANNELS, 
            rate=SAMPLE_RATE, 
            output=True, 
            frames_per_buffer=1024
        )

    def _play_worker(self):
        while self.is_playing:
            try:
                data = self.audio_queue.get(timeout=0.1)
                if data and self.stream: 
                    self.stream.write(data)
            except queue.Empty: 
                continue
            except Exception as e: 
                print(f"❌ 播放音频错误：{e}")
                self._init_stream()

    def add_audio(self, data):
        self.audio_queue.put(data)

    def clear_queue(self):
        with self.audio_queue.mutex: 
            self.audio_queue.queue.clear()

    def is_busy(self):
        return not self.audio_queue.empty()

    def stop(self):
        self.is_playing = False
        if self.play_thread: 
            self.play_thread.join(timeout=1)
        if self.stream:
            try: 
                self.stream.stop_stream()
                self.stream.close()
            except: 
                pass
        self.pyaudio_instance.terminate()

class DuplexCallback(MultiModalCallback):
    def __init__(self, manager, robot_controller, camera_controller):
        self.manager = manager
        self.robot_controller = robot_controller
        self.camera_controller = camera_controller
        self.audio_player = AudioPlayer(self)
        self.is_responding = False
        self.response_text = ""
        self.last_user_text = ""
        self.last_ai_response = ""
        self.is_sending_silence = False
        self.silence_thread = None
        self.vqa_context_active = False
        self.last_vqa_response = None
        # 添加请求控制标志位和计时器
        self.request_lock = threading.Lock()
        self.last_request_time = 0
        self.min_request_interval = 1.0  # 请求最小间隔时间（秒）
        self.is_request_pending = False  # 标记是否有待处理的请求
        self.pending_request_type = None  # 待处理请求类型
        self.pending_request_args = None  # 待处理请求参数
        self.request_timer = None  # 定时器对象
        self.numeric_commands = [
            "move_to_neutral", "move_joint",  "move_joint_direction"
        ]
        self.skill_map = {
            "move_to_top_view": self.robot_controller.move_to_top_view,
            "move_to_neutral": self.robot_controller.move_to_neutral,
            "move_joint": self.robot_controller.move_joint,
            "move_joint_direction": self.robot_controller.move_joint_direction,
            "move_to_catch": self.robot_controller.move_to_catch,
            "perform_greeting": self.robot_controller.perform_greeting,
            "perform_nod": self.robot_controller.perform_nod,
            "perform_head_shake": self.robot_controller.perform_head_shake,
            "perform_dance": self.robot_controller.perform_dance,
            "perform_random_action": self.robot_controller.perform_random_action,
            "perform_subtle_look": self.robot_controller.perform_subtle_look,
            "perform_gentle_breath": self.robot_controller.perform_gentle_breath,
        }
        
    # 添加安全请求方法
    def _safe_request(self, request_type, *args, **kwargs):
        with self.request_lock:
            current_time = time.time()
            time_since_last_request = current_time - self.last_request_time
            
            # 如果距离上次请求时间不够，则排队等待
            if time_since_last_request < self.min_request_interval:
                if self.is_request_pending:
                    print(f"⚠️ 已有待处理请求，当前请求将被丢弃: {request_type}")
                    return False
                
                # 设置待处理请求
                self.is_request_pending = True
                self.pending_request_type = request_type
                self.pending_request_args = (args, kwargs)
                
                # 计算需要等待的时间
                wait_time = self.min_request_interval - time_since_last_request
                print(f"⏳ 请求过于频繁，{wait_time:.2f}秒后自动发送: {request_type}")
                
                # 创建定时器
                if self.request_timer:
                    self.request_timer.cancel()
                self.request_timer = threading.Timer(wait_time, self._process_pending_request)
                self.request_timer.daemon = True
                self.request_timer.start()
                return True
            
            # 可以立即执行请求
            try:
                self._execute_request(request_type, *args, **kwargs)
                self.last_request_time = current_time
                return True
            except Exception as e:
                print(f"❌ 执行请求失败: {e}")
                return False
    
    def _process_pending_request(self):
        with self.request_lock:
            if not self.is_request_pending:
                return
            
            print(f"⏯️ 执行待处理请求: {self.pending_request_type}")
            try:
                args, kwargs = self.pending_request_args
                self._execute_request(self.pending_request_type, *args, **kwargs)
                self.last_request_time = time.time()
            except Exception as e:
                print(f"❌ 执行待处理请求失败: {e}")
            finally:
                # 重置待处理状态
                self.is_request_pending = False
                self.pending_request_type = None
                self.pending_request_args = None
    
    def _execute_request(self, request_type, *args, **kwargs):
        if request_type == "request_to_respond":
            self.manager.convo.request_to_respond(*args, **kwargs)
        elif request_type == "interrupt":
            self.manager.convo.interrupt()
        # 可根据需要添加其他请求类型

    def _send_silence_worker(self):
        bytes_per_frame = CHANNELS * SAMPLE_WIDTH
        silence_packet = b'\x00' * (AUDIO_FRAME_SIZE * bytes_per_frame)
        print("❤️ [HB] 开始发送静音心跳包以保持连接...")
        while self.is_sending_silence:
            try:
                self.manager.convo.send_audio_data(silence_packet)
                time.sleep(0.04)
            except Exception as e:
                print(f"❌ [HB] 发送静音时出错: {e}"); break
        print("💔 [HB] 静音心跳包已停止。")

    def _handle_vqa_request(self):
        self.vqa_context_active = True
        print("📝 [CTX] VQA 上下文已激活。")
        self.is_sending_silence = True
        self.silence_thread = threading.Thread(target=self._send_silence_worker)
        self.silence_thread.daemon = True
        self.silence_thread.start()
        print("📸 [VQA] 流程启动，正在拍照...")
        image_b64 = self.camera_controller.capture_image_base64()
        
        if image_b64:
            self.is_sending_silence = False
            if self.silence_thread: self.silence_thread.join(timeout=0.5)
            vqa_prompt = self.last_user_text
            print(f"📸 [VQA] 发送图文请求，使用用户的原始问题: '{vqa_prompt}'")
            image_param = {"type": "base64", "value": image_b64}
            vqa_params = RequestToRespondParameters(images=[image_param])
            # 使用安全请求方法替代直接调用
            self._safe_request("request_to_respond", "prompt", vqa_prompt, parameters=vqa_params)
        else:
            self.is_sending_silence = False
            if self.silence_thread: self.silence_thread.join(timeout=0.5)
            print("❌ [VQA] 图像捕获失败，VQA流程取消。")
            self.vqa_context_active = False

    def _get_param_value(self, params_list, param_name):
        for param in params_list:
            if param.get("name") == param_name:
                return param.get("normValue")
        return None

    def _extract_number(self, text_with_units):
        if isinstance(text_with_units, (int, float)): return text_with_units
        if not isinstance(text_with_units, str): return None
        numbers = re.findall(r'\d+\.?\d*|\d+', text_with_units)
        if numbers: return float(numbers[0])
        return None
    
    def on_responding_content(self, payload):
        if not payload or "output" not in payload: return
        output = payload.get("output", {})
        text_chunk = output.get("text", "")
        match = re.search(r"robot_write\(text=(['\"])(.*?)\1\)", text_chunk)
        if match:
            text_to_write = match.group(2)
            print(f"🦾 [ROBOT] (从文本中捕获) 分发指令 [robot_write] (参数: '{text_to_write}')")
            self.robot_controller.stop_current_task()
            self.manager.dispatch_robot_task(self.robot_controller.robot_write, args=(text_to_write,), is_long_task=True)
            return

        extra_info = output.get("extra_info", {})
        commands_str = extra_info.get("commands")
        if commands_str:
            try:
                commands_list = json.loads(commands_str)
                for command in commands_list:
                    self.robot_controller.stop_current_task()
                    time.sleep(0.1) 
                    command_name = command.get("name")
                    if command_name == "visual_qa":
                        print("‼️ [CMD] 检测到 visual_qa 指令！")
                        vqa_thread = threading.Thread(target=self._handle_vqa_request)
                        vqa_thread.daemon = True
                        vqa_thread.start()
                        return
                        
                    if command_name in self.skill_map:
                        skill_func = self.skill_map[command_name]
                        params = command.get("params", [])
                        
                        # 多参数处理
                        if command_name == "move_joint_direction":
                            # 提取关节号
                            joint_id_param = self._get_param_value(params, "joint_id")
                            joint_id = int(self._extract_number(joint_id_param)) if joint_id_param else None
                            
                            # 提取方向
                            direction_param = self._get_param_value(params, "direction")
                            direction = direction_param if direction_param else "正"
                            
                            # 提取角度
                            distance_param = self._get_param_value(params, "distance")
                            distance = self._extract_number(distance_param) if distance_param else None
                            
                            if joint_id is not None and distance is not None:
                                print(f"\n🦾 [ROBOT] 分发指令 [move_joint_direction] (关节: {joint_id}, 方向: {direction}, 角度: {distance})")
                                self.manager.dispatch_robot_task(
                                    target_func=skill_func,
                                    args=(joint_id, direction, distance),
                                    is_long_task=False
                                )
                            else:
                                print(f"❌ [ROBOT] move_joint_direction 参数不完整: 关节={joint_id}, 方向={direction}, 角度={distance}")
                            return
                        # 处理 move_joint 命令，需要两个参数 
                        elif command_name == "move_joint":
                            # 提取关节号
                            joint_id_param = self._get_param_value(params, "joint_id")
                            joint_id = int(self._extract_number(joint_id_param)) if joint_id_param else None
                            
                            # 提取角度
                            angle_param = self._get_param_value(params, "angle")
                            angle = self._extract_number(angle_param) if angle_param else None
                            
                            if joint_id is not None and angle is not None:
                                print(f"\n🦾 [ROBOT] 分发指令 [move_joint] (关节: {joint_id}, 角度: {angle})")
                                self.manager.dispatch_robot_task(
                                    target_func=skill_func,
                                    args=(joint_id, angle),
                                    is_long_task=False
                                )
                            else:
                                print(f"❌ [ROBOT] move_joint 参数不完整: 关节={joint_id}, 角度={angle}")
                            return
                        # 处理 move_to_catch 命令，需要两个参数instance_A和location_B 
                        elif command_name == "move_to_catch":
                            # 提取 instance_A 参数
                            instance_A_param = self._get_param_value(params, "instance_A")

                            # 提取 location_B 参数
                            location_B_param = self._get_param_value(params, "location_B")

                            if instance_A_param and location_B_param:
                                print(f"\n🦾 [ROBOT] 分发指令 [move_to_catch] (instance_A: {instance_A_param}, location_B: {location_B_param})")
                                self.manager.dispatch_robot_task(
                                    target_func=skill_func,
                                    args=(instance_A_param, location_B_param),
                                    is_long_task=True
                                )
                            else:
                                print("❌ [ROBOT] move_to_catch 缺少 instance_A 和 location_B 参数")
                            return
                        # 原有的单参数处理
                        else:
                            args_for_thread = ()
                            final_param = None
                            sig = inspect.signature(skill_func)
                            if len(sig.parameters) > 0:
                                param_name = list(sig.parameters.keys())[0]
                                original_param_value = self._get_param_value(params, param_name)
                                if command_name in self.numeric_commands:
                                    final_param = self._extract_number(original_param_value)
                                    log_msg = f"\n🦾 [ROBOT] 分发指令 [{command_name}] (净化后: {final_param})"
                                    if final_param is None:
                                        print(f" (无法从参数 '{original_param_value}' 中提取有效数字，跳过执行)")
                                        return
                                else:
                                    final_param = original_param_value
                                    log_msg = f"\n🦾 [ROBOT] 分发指令 [{command_name}] (参数: '{final_param}')"
                                args_for_thread = (final_param,)
                            else:
                                log_msg = f"\n🦾 [ROBOT] 分发指令 [{command_name}] (无参数)"
                            print(log_msg)
                            is_long_task = (command_name == "robot_write")
                            self.manager.dispatch_robot_task(
                                target_func=skill_func, 
                                args=args_for_thread,
                                is_long_task=is_long_task
                            )
                            return
            except Exception as e: print(f"❌ 指令分发或执行失败: {e}")

        if not self.is_responding and not output.get("finished", False): self.response_text = ""
        self.response_text += text_chunk
        if output.get("finished", False) and self.response_text:
            print(f"\n🤖 AI回复：{self.response_text}")
            self.last_ai_response = self.response_text
            if self.vqa_context_active:
                self.last_vqa_response = self.response_text
                print(f"📝 [CTX] VQA 响应已捕获，可用于后续写入。")

    def on_speech_content(self, payload):
        if self.manager.is_in_autonomous_speech:
            return

        if payload and payload.get("output"):
            output = payload["output"]
            text = output.get("text", "")
            finished = output.get("finished", False)
            if text and not finished:
                print(f"\r🎤 识别中：{text}", end="", flush=True)
            
            if finished:
                print(f"\n🎤 用户说：{text}")
                self.last_user_text = text
                move_keywords = ["前", "后", "左", "右", "上", "下", "移动", "前进", "后退"]
                is_move_command = any(keyword in text for keyword in move_keywords)

                catch_keywords = ["抓", "catch", "抓起来", "拿","放"]
                is_catch_command = any(keyword in text for keyword in catch_keywords)

                if self.vqa_context_active and self.last_vqa_response and is_catch_command:
                    self.robot_controller.stop_current_task()
                    self.manager.dispatch_robot_task(self.robot_controller.move_to_catch, args=(text,), is_long_task=True)
                    self.vqa_context_active = False
                    self.last_vqa_response = None
                    print("📝 [CTX] VQA 上下文已重置。")
                    # 使用安全请求方式
                    self._safe_request("interrupt")
                    return

                # write_keywords = ["写", "write", "记下来", "记录", "把它写"]
                # is_write_command = any(keyword in text for keyword in write_keywords)

                # if self.vqa_context_active and self.last_vqa_response and is_write_command:
                #     print("✍️ [CTX] 检测到VQA后的写入指令...")
                #     self.robot_controller.stop_current_task()
                #     text_to_write = self.last_vqa_response
                #     self.manager.dispatch_robot_task(self.robot_controller.robot_write, args=(text_to_write,), is_long_task=True)
                #     self.vqa_context_active = False
                #     self.last_vqa_response = None
                #     print("📝 [CTX] VQA 上下文已重置。")
                #     self.manager.convo.interrupt()
                #     return

    def on_error(self, error):
        err = str(error)
        if "Internal server error" in err or "ping/pong" in err or "Task 'failed'" in err: self.manager.need_reconnect = True
        # 注释空闲模式相关代码
        # if self.manager.is_in_idle_mode:
        #     self.manager.idle_speak_complete.set()

    def on_connected(self): print("✅ WebSocket 已连接")
    def on_started(self, dialog_id: str): self.manager.dialog_id = dialog_id
    def on_state_changed(self, state: DialogState): print(f"📌 状态变化：{state}"); self.manager.current_state = state
    def on_speech_audio_data(self, data: bytes): self.audio_player.add_audio(data)
    
    def on_responding_started(self):
        self.is_responding = True
        self.response_text = ""
        print("\n🔊 AI开始回复...")
        # 注意：ElephantRobot的start_talking_animation不接受audio_player参数
        # self.robot_controller.start_talking_animation()

    def on_responding_ended(self, payload):
        self.is_responding = False
        # 等待动画完成
        time.sleep(0.5) 
        self.robot_controller.stop_animation(return_to_base=True)
        # 注释空闲模式相关代码
        # if self.manager.is_in_idle_mode:
        #     self.manager.idle_speak_complete.set()
        
    def on_request_accepted(self): 
        print("\n✅ 打断成功")
        self.audio_player.clear_queue()
        self.robot_controller.stop_animation(return_to_base=False)

    def on_close(self, code, msg):
        print(f"\n📌 连接关闭：{code} — {msg}")
        if code != 1000: self.manager.need_reconnect = True
        # 注释空闲模式相关代码
        # if self.manager.is_in_idle_mode:
        #     self.manager.idle_speak_complete.set()

    def cleanup(self):
        if self.audio_player: 
            self.audio_player.stop()
        if self.robot_controller: 
            self.robot_controller.stop_current_task()
        # 取消任何待处理的请求计时器
        if hasattr(self, 'request_timer') and self.request_timer:
            self.request_timer.cancel()
            
class DuplexVoiceAssistant:
    def __init__(self, robot_controller, camera_controller):
        self.robot_controller = robot_controller
        self.camera_controller = camera_controller
        self.need_reconnect, self.current_state = False, None
        self.pyaudio_instance = pyaudio.PyAudio()
        self.in_stream, self.audio_thread = None, None
        self.is_running = True
        self.last_interaction_time = time.time()
        self.is_in_idle_mode = False
        self.is_robot_busy = False
        self.idle_check_interval = 2
        self.idle_timeout = 10
        self.last_vqa_idle_time = 0
        self.idle_vqa_interval = 20
        self.idle_speak_complete = threading.Event()
        self.is_in_autonomous_speech = False
        self._build_dialog()
        self._init_audio_input()
        
    def _init_audio_input(self):
        if self.in_stream:
            try: self.in_stream.stop_stream(); self.in_stream.close()
            except: pass
        self.in_stream = self.pyaudio_instance.open(
            format=self.pyaudio_instance.get_format_from_width(SAMPLE_WIDTH),
            channels=CHANNELS, rate=SAMPLE_RATE, input=True, frames_per_buffer=AUDIO_FRAME_SIZE
        )
    
    def _build_dialog(self):
        up = Upstream(type="AudioAndVideo", mode="duplex", audio_format="pcm")
        down = Downstream(voice=VOICE_NAME, sample_rate=SAMPLE_RATE, audio_format="pcm")
        client = ClientInfo(user_id="sdk_user", device=Device(uuid="sdk_device"))
        params = RequestParameters(upstream=up, downstream=down, client_info=client)
        if hasattr(self, 'callback') and self.callback: self.callback.cleanup()
        self.callback = DuplexCallback(self, self.robot_controller, self.camera_controller)
        self.convo = MultiModalDialog(
            app_id=APP_ID, workspace_id=WORKSPACE_ID, api_key=API_KEY, url=WEBSOCKET_URL,
            request_params=params, multimodal_callback=self.callback, model=MODEL_NAME
        )
    
    def _recording_worker(self):
        recording = False
        print("\n💡 按住空格键说话，松开结束；按空格键可打断 AI 或唤醒机器人")
        while self.is_running:
            try:
                if keyboard.is_pressed('space'):
                    self.last_interaction_time = time.time()

                    if self.callback.is_responding:
                        print("\n🔸 用户打断 AI（Space）...")
                        # 使用安全请求机制
                        self.callback._safe_request("interrupt")
                        time.sleep(0.1)
                    if not recording:
                        print("\n🎙️ 开始识别...")
                        self.convo.start_speech()
                        recording = True
                    
                    data = self.in_stream.read(AUDIO_FRAME_SIZE, exception_on_overflow=False)
                    self.convo.send_audio_data(data)
                else:
                    if recording:
                        print("\n⏹️ 结束识别")
                        self.convo.stop_speech()
                        recording = False
                
                if keyboard.is_pressed('esc'):
                    print("\n🛑 ESC 退出")
                    self.is_running = False
                    break
                
                time.sleep(0.01)
            except Exception as e:
                print(f"❌ 录音线程错误：{e}")
                time.sleep(0.1)

    def dispatch_robot_task(self, target_func, args=(), is_long_task=False):
        def get_task_desc():
            # 生成更友好的任务描述
            func_name = target_func.__name__
            if args:
                arg_str = ', '.join([str(a) for a in args])
                return f"{func_name}({arg_str})"
            else:
                return func_name

        def task_wrapper():
            # 任务前AI回复
            task_desc = get_task_desc()
            try:
                # 使用安全请求机制
                self.callback._safe_request("request_to_respond", "prompt", f"好的，我将执行 {task_desc}")
            except Exception as e:
                print(f"⚠️ 任务前AI回复失败: {e}")
            if is_long_task:
                self.is_robot_busy = True
                print(f"🚦 机器人进入忙碌状态，执行长任务: {target_func.__name__}")
            try:
                target_func(*args)
            finally:
                if is_long_task:
                    self.is_robot_busy = False
                    print(f"🚥 机器人忙碌状态解除。")
                # 任务后AI回复
                try:
                    # 使用安全请求机制
                    self.callback._safe_request("request_to_respond", "prompt", "执行完毕")
                except Exception as e:
                    print(f"⚠️ 任务后AI回复失败: {e}")
        
        thread = threading.Thread(target=task_wrapper, daemon=True)
        thread.start()

    # 注释整个 enter_idle_mode 方法
    # def enter_idle_mode(self):
    #     self.is_in_idle_mode = True
    #     print(f"\n😴 {self.idle_timeout}秒无交互，进入空闲模式...")
    #     self.robot_controller.back_zero()
    #     self.last_vqa_idle_time = time.time()
    #     time.sleep(1)
    #     idle_actions = self.robot_controller.get_idle_action_list()

    #     while self.is_in_idle_mode and self.is_running:
    #         if not self.is_in_idle_mode: break
            
    #         if self.callback.is_responding:
    #             time.sleep(0.5)
    #             continue
            
    #         if time.time() - self.last_vqa_idle_time > self.idle_vqa_interval:
    #             self.perform_autonomous_vqa()
    #             self.last_vqa_idle_time = time.time()
    #         else:
    #             action = random.choice(idle_actions)
    #             action()
            
    #         pause_duration = random.uniform(2.0, 4.0)
    #         time.sleep(pause_duration)

    #     print("🧠 空闲模式结束，返回待命状态。")

    # 注释整个 perform_autonomous_vqa 方法
    # def perform_autonomous_vqa(self):
    #     print("🤔 (空闲) 我自己找点乐子...")
        
    #     self.is_in_autonomous_speech = True

    #     self.callback.is_sending_silence = True
    #     silence_thread = threading.Thread(target=self.callback._send_silence_worker, daemon=True)
    #     silence_thread.start()

    #     self.robot_controller.move_to_random_vqa_point()
    #     image_b64 = self.camera_controller.capture_image_base64()
        
    #     if not image_b64:
    #         print("❌ (空闲) 拍照失败，跳过本次VQA。")
    #         self.callback.is_sending_silence = False
    #         silence_thread.join(timeout=0.5)
    #         self.robot_controller.back_zero()
    #         self.is_in_autonomous_speech = False
    #         return
            
    #     print("🖼️ (空闲) 看看这是什么... 并想一句俏皮话...")
    #     comment_text = ""
    #     try:
    #         prompt = "你对当下照片生成一个让下一个大模型基于你看到的细节，仿佛是他看到的一样去评论20个字的prompt，把你在照片中看到的细节，传给下一个大模型，然后你也要告诉下一个大模型用幽默的语气评论这个照片中你提取出来的细节。。"
    #         messages = [{'role': 'user', 'content': [
    #             {'image': f'data:image/jpeg;base64,{image_b64}'},
    #             {'text': prompt}
    #         ]}]
    #         response = MultiModalConversation.call(model='qwen-vl-plus', messages=messages, api_key=API_KEY)
    #         comment_text = response.output.choices[0].message.content[0]['text']
    #     except Exception as e:
    #         print(f"❌ (空闲) VQA API 调用失败: {e}")
    #         self.callback.is_sending_silence = False
    #         silence_thread.join(timeout=0.5)
    #         self.robot_controller.back_zero()
    #         self.is_in_autonomous_speech = False
    #         return
        
    #     self.callback.is_sending_silence = False
    #     silence_thread.join(timeout=0.5)
        
    #     if self.current_state == DialogState.RESPONDING:
    #         print("ℹ️ 检测到系统在响应（可能为空），发送打断信号以重置状态...")
    #         try:
    #             self.convo.interrupt()
    #         except Exception as e:
    #             print(f"⚠️ 发送打断信号时出错: {e}")

    #     wait_start_time = time.time()
    #     while self.current_state != DialogState.LISTENING and (time.time() - wait_start_time) < 5.0:
    #         print(f"⏳ 等待系统返回 LISTENING 状态... 当前: {self.current_state}")
    #         time.sleep(0.2)

    #     if self.current_state != DialogState.LISTENING:
    #         print(f"❌ 等待 LISTENING 超时，无法继续。当前状态: {self.current_state}")
    #         self.robot_controller.back_zero()
    #         self.is_in_autonomous_speech = False
    #         return
        
    #     try:
    #         if comment_text:
    #             print(f"💬 (空闲) 准备通过主流程说出: "{comment_text}"")
    #             if self.current_state == DialogState.LISTENING and not self.callback.is_responding:
    #                 self.idle_speak_complete.clear()
    #                 self.convo.request_to_respond("prompt", comment_text)
    #                 print("...⏳ 等待自言自语完成...")
    #                 finished_in_time = self.idle_speak_complete.wait(timeout=20) 
    #                 if not finished_in_time:
    #                     print("⚠️ 等待自言自语超时！")
    #                     self.convo.interrupt()
    #             else:
    #                 print(f"⚠️ (空闲) 主对话系统未就绪，跳过本次自言自语。当前状态: {self.current_state}, 是否响应中: {self.callback.is_responding}")
    #     finally:
    #         self.is_in_autonomous_speech = False

    #     time.sleep(1)
    #     self.robot_controller.back_zero()

    def start(self):
        print("🔗 启动助手...")
        try: self.convo.start(""); time.sleep(1); return True
        except Exception as e: print(f"❌ 启动失败：{e}"); return False
        
    def start_continuous_recording(self):
        self.audio_thread = threading.Thread(target=self._recording_worker, daemon=True)
        self.audio_thread.start()
        
    def run(self):
        if not self.start(): return
        start_time = time.time()
        while time.time() - start_time < 10:
            if self.current_state == DialogState.LISTENING: break
            time.sleep(0.1)
        if self.current_state != DialogState.LISTENING:
            print("❌ 系统在10秒内未就绪，请检查网络或API-KEY。"); self.cleanup(); return
            
        print("✅ 系统就绪，开始多轮对话"); self.start_continuous_recording()
        try:
            while self.is_running:
                if self.need_reconnect:
                    print("\n⚡ 检测到需要重连，将在5秒后尝试..."); time.sleep(5)
                    self.reconnect()
                
                # 注释空闲模式相关代码
                # is_ready_for_idle = (not self.is_in_idle_mode and 
                #                     not self.is_robot_busy and
                #                     not self.callback.is_responding)
                
                # if is_ready_for_idle and (time.time() - self.last_interaction_time > self.idle_timeout):
                #     self.enter_idle_mode()
                
                time.sleep(self.idle_check_interval)
        except KeyboardInterrupt:
            print("\n🛑 手动中断")
        finally:
            self.cleanup()

    def reconnect(self):
        print("🔄 正在执行重连..."); self.need_reconnect = False; self.is_running = False
        if self.audio_thread and self.audio_thread.is_alive(): self.audio_thread.join(timeout=1)
        try: self.convo.stop()
        except Exception as e: print(f"⚠️ 停止旧连接时出错: {e}")
        time.sleep(1)
        print("🔄 重新初始化组件..."); self.is_running = True
        self._build_dialog(); self._init_audio_input()
        if self.start():
            start_time = time.time()
            while time.time() - start_time < 10:
                if self.current_state == DialogState.LISTENING: break
                time.sleep(0.1)
            if self.current_state == DialogState.LISTENING:
                self.start_continuous_recording(); print("✅ 重连成功，系统已就绪！"); return
        print("❌ 重连失败，程序将退出。"); self.is_running = False
        
    def cleanup(self):
        print("\n🧹 清理资源..."); self.is_running = False
        self.robot_controller.stop_current_task()
        if self.audio_thread and self.audio_thread.is_alive(): self.audio_thread.join(timeout=1)
        try: self.convo.stop()
        except: pass
        if self.in_stream:
            try: self.in_stream.stop_stream(); self.in_stream.close()
            except: pass
        if hasattr(self, 'callback') and self.callback: self.callback.cleanup()
        self.pyaudio_instance.terminate()
        print("✅ 清理完成")